package ar.com.e2solver.solver.operator.impl;

import java.util.ArrayList;
import java.util.List;

import ar.com.e2solver.exception.BusinessException;
import ar.com.e2solver.model.Coordinate;
import ar.com.e2solver.model.Piece;
import ar.com.e2solver.model.Puzzle;
import ar.com.e2solver.solver.Individual;
import ar.com.e2solver.solver.RandomConstants;
import ar.com.e2solver.solver.operator.MutationOperator;

public final class SwapMutationOperator implements MutationOperator {
	
	private static SwapMutationOperator instance = new SwapMutationOperator();
	
	private SwapMutationOperator(){}

	public static SwapMutationOperator getInstance() {
		return instance;
	}

	public Individual operate(Individual individual) throws BusinessException {
		Puzzle puzzle = new Puzzle(individual.getPuzzle());
		
		Coordinate c1 = RandomConstants.getRandomCoordinate(puzzle.getRowsNumber(), puzzle.getColumnNumbers());
		Piece piece1 = puzzle.getPieceAt(c1);
		
		if(piece1.isACornerPiece()){
			List<Coordinate> coordinates = getCornerCoordinates(puzzle.getRowsNumber(), puzzle.getColumnNumbers()); 			
			coordinates.remove(c1);
			int randomIndex = RandomConstants.RANDOM.nextInt(coordinates.size());
			Coordinate c2 = coordinates.get(randomIndex);
			
			puzzle.swapPositions(c1, c2);
			
			// Acomoda los bordes de las esquinas
			rotateForCorner(c1, puzzle);
			rotateForCorner(c2, puzzle);

		} else if(piece1.isABorderPiece()){
			List<Coordinate> coordinates = getBorderCoordinates(puzzle.getRowsNumber(), puzzle.getColumnNumbers());
			coordinates.remove(c1);
			int randomIndex = RandomConstants.RANDOM.nextInt(coordinates.size());
			Coordinate c2 = coordinates.get(randomIndex);
			
			puzzle.swapPositions(c1, c2);
		
			// Acomoda los bordes de los bordes
			rotateForBorder(c1, puzzle);
			rotateForBorder(c2, puzzle);			

		} else {
			int x2Random = RandomConstants.RANDOM.nextInt(puzzle.getRowsNumber() - 2) + 1;
			int y2Random = RandomConstants.RANDOM.nextInt(puzzle.getColumnNumbers() - 2) + 1;
			Coordinate c2 = new Coordinate(x2Random, y2Random);
			puzzle.swapPositions(c1, c2);
		}
		
		return new Individual(puzzle);
	}

	private void rotateForBorder(Coordinate c, Puzzle p) {
		int i = c.getX();
		int j = c.getY();
		Piece piece = p.getPieceAt(c);
		
		if(i == 0 ){
			while(!piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else if(i > j){
			while(!piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}	
		}
	}

	private void rotateForCorner(Coordinate c, Puzzle p) {
		int i = c.getX();
		int j = c.getY();
		Piece piece = p.getPieceAt(c);
		
		if(i == 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i == 0 && j != 0){
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i != 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		}
	}
	
	private List<Coordinate> getCornerCoordinates(int row, int col) {
		List<Coordinate> coordinates = new ArrayList<Coordinate>();
		coordinates.add(new Coordinate(0,0));
		coordinates.add(new Coordinate(0, col -1));
		coordinates.add(new Coordinate(row -1, 0));
		coordinates.add(new Coordinate(row -1, col -1));
		return coordinates;
	}
	
	private List<Coordinate> getBorderCoordinates(int row, int col) {
		List<Coordinate> borderCoordinates = new ArrayList<Coordinate>();

		for(int i = 0; i < row; i++){
			borderCoordinates.add(new Coordinate(i, 0));
			borderCoordinates.add(new Coordinate(i, col - 1));
		}
		
		for(int j = 0; j < col; j++){
			borderCoordinates.add(new Coordinate(0, j));
			borderCoordinates.add(new Coordinate(row - 1, j));
		}
		
		// Saca las esquinas
		borderCoordinates.removeAll(getCornerCoordinates(row, col));
		
		return borderCoordinates;
	}	

}
